package com.example.client.netty;

import com.example.commons.model.LoginReqMsg;
import com.example.commons.MsgPool;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @author cdw
 **/
@Slf4j
@Component
public class NettyClient {
    private final EventLoopGroup group = new NioEventLoopGroup();
    private final Bootstrap bootstrap = new Bootstrap();
    private long connCount = 0;
    @Value("${app.client-id}")
    private String clientId;
    @Value("${app.tcp.host}")
    private String host;
    @Value("${app.tcp.port}")
    private Integer port;

    @Autowired
    private MsgPool pool;
    @Autowired
    private NettyClientInitializer nettyClientInitializer;

    @PostConstruct
    public void start() {
        bootstrap.group(group)
                //该参数的作用就是禁止使用Nagle算法，使用于小数据即时传输
                .option(ChannelOption.TCP_NODELAY, true)
                .channel(NioSocketChannel.class)
                .handler(nettyClientInitializer);
        connect();
    }

    @PreDestroy
    public void shutdown() {
        try {
            Channel channel = pool.getServerChannel();
            if (channel != null && channel.isActive()) {
                channel.close().sync();
            }
            this.group.shutdownGracefully();
        } catch (InterruptedException e) {
            log.error(e.getMessage(), e);
        }
    }

    public void connect() {
        connCount = 0;
        doConnect();
    }

    private void doConnect() {
        bootstrap.connect(host, port).addListener((ChannelFutureListener) future -> {
            if (future.isSuccess()) {
                Channel channel = future.channel();
                log.info(new Date() + ": 连接成功!");

                LoginReqMsg loginReqMsg = new LoginReqMsg();
                loginReqMsg.setClientId(clientId);

                pool.sendMsg(channel, loginReqMsg);

                log.info("发送登录消息");
            } else {
                // 第几次重连
                long order = ++connCount;
                // 本次重连的间隔

                log.info(new Date() + ": 连接失败，第 {} 次重连", order);
                bootstrap.config().group().schedule(this::doConnect, 5, TimeUnit.SECONDS);
            }
        });
    }
}